﻿using System;
using System.Collections;
using System.ServiceModel;
using System.Windows.Forms;
using ClientApp.Classes;
using ClientApp.Classes.XtraTile;
using ClientApp.Gestori;
using ClientApp.UC;
using GianosNET.ClientApp.Properties;
using GianosNET.Common.Classes;

namespace ClientApp
{
    public partial class Main : Form
    {
        private string server;
        private string username;
        private string accessPwd;

        //private RemotedServicesCommon.Interfaces.IRemotedServiceManager mgr;
        //private Dictionary<string, RemotedClass> obj;

        private string serverVersion;
        private ClientApp.Classes.RemotedService.RemotedServiceManagerClient client;
        

        #region Delegati Thread-safe

        private delegate void ThreadSafeLogUpdateDlg(Guid id, string msg);

        private delegate void ThreadSafeServiceStatusDlg(Guid id, RemotedClass.RemotedClassStatusEnum status);

        private delegate void ThreadSafeLoopingStatusDlg(Guid id, LoopingService.LoopingStateEnum status);

        private delegate void ThreadSafeIdleCountdownDlg(Guid id, int secs);

        #endregion

        //public delegate void MyEventCallbackHandler(Guid id, string msg);
        public event CommonHandlers.LogChangeEventHandler LogChangeCallbackEvent;
        public event CommonHandlers.ServiceStatusChangedEventHandler ServiceStatusChangeCallbackEvent;
        public event CommonHandlers.LoopingStatusChangedEventHandler LoopingStatusChangeCallbackEvent;
        public event CommonHandlers.IdleCountdownChangedEventHandler IdleCountdownChangeCallbackEvent;
        public event CommonHandlers.ForceFullClientRefresh ForceFullClientRefreshCallbackEvent;

        [CallbackBehavior(UseSynchronizationContext = false)]
        public class ServiceCallback : ClientApp.Classes.RemotedService.IRemotedServiceManagerCallback
        {
            public void LogUpdate(Guid id, string msg)
            {
                if (f != null && f.LogChangeCallbackEvent != null)
                    f.LogChangeCallbackEvent(id, msg);
            }

            public void LoopingIdleCountdown(Guid id, int secs)
            {
                if (f != null && f.IdleCountdownChangeCallbackEvent != null)
                    f.IdleCountdownChangeCallbackEvent(id, secs);
            }

            public void ServiceStatusChanged(Guid id, RemotedClass.RemotedClassStatusEnum status)
            {
                if (f != null && f.ServiceStatusChangeCallbackEvent != null)
                    f.ServiceStatusChangeCallbackEvent(id, status);
            }

            public void LoopingStatusChanged(Guid id, LoopingService.LoopingStateEnum status)
            {
                if (f != null && f.LoopingStatusChangeCallbackEvent != null)
                    f.LoopingStatusChangeCallbackEvent(id, status);
            }

            public void ForceFullClientRefresh()
            {
                if (f != null && f.ForceFullClientRefreshCallbackEvent != null)
                    f.ForceFullClientRefreshCallbackEvent();
            }

            private Main f;

            public ServiceCallback(Main f)
            {
                this.f = f;
            }
        }

        //private extTooltip tt;
        private Hashtable cfg;

        private ServiceInfoExtCollection svcInfo;
        private string token;


        private ModuleChanger changer;

        public Main()
        {
            InitializeComponent();

            lblVersion.Text = string.Format("v. client {0} / server {1}", Application.ProductVersion, "???");

            changer = new ModuleChanger(this, cTile, pnlDetail, 4, panelControl1.Height, status.Height);
        }

        private void ConnectToServer(string srv)
        {
            try
            {
                InstanceContext context = new InstanceContext(new ServiceCallback(this));

                NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
                binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
                binding.MaxReceivedMessageSize = Settings.Default.MaxReceivedMessageSize;

                EndpointAddress ea = new EndpointAddress(new Uri(srv));
                client = new ClientApp.Classes.RemotedService.RemotedServiceManagerClient(context, binding, ea);

                server = srv;

                while (!client.Authenticate(out token, username, accessPwd))
                {
                    try
                    {
                        client.Close();
                    }
                    finally
                    {
                        client.Abort();
                    }

                    MessageBox.Show("Autenticazione non riuscita", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    if (!Login())
                    {
                        Application.Exit();
                        return;
                    }

                    ea = new EndpointAddress(new Uri(server));
                    client = new ClientApp.Classes.RemotedService.RemotedServiceManagerClient(context, binding, ea);
                }

                LogChangeCallbackEvent += UpdateLog;
                ServiceStatusChangeCallbackEvent += Main_ServiceStatusChangeCallbackEvent;
                LoopingStatusChangeCallbackEvent += Main_LoopingStatusChangeCallbackEvent;
                IdleCountdownChangeCallbackEvent += Main_IdleCountdownChangeCallbackEvent;
                ForceFullClientRefreshCallbackEvent += Main_ForceFullClientRefreshCallbackEvent; 

                lblStatus.Text = server;

                if (client.State == CommunicationState.Opened)
                {
                    svcInfo = new ServiceInfoExtCollection();
                    ServiceInfo[] lst = client.GetServiceList();
                    serverVersion = client.GetServerVersion();

                    lblVersion.Text = string.Format("v. client {0} / server {1}", Application.ProductVersion,
                                                    serverVersion);

                    foreach (ServiceInfo si in lst)
                        svcInfo.Add(new Classes.ServiceInfoExt(si));

                    if (!string.IsNullOrEmpty(token))
                        // registra i servizi remoti
                        foreach (ServiceInfoExt si in svcInfo)
                        {
                            RegisterRemoteObject(si);
                            cTile.Refresh();

                            // Setta lo stato inizialetabControl
                            Main_ServiceStatusChangeCallbackEvent(si.SvcId, si.ServiceStatus);
                        }

                    SignalSvcStatus();
                }
                else
                {
                    MessageBox.Show(
                        string.Format("La connessione non può essere stabilita.\nStato della connessione: {0}",
                                      client.State));
                    client.Abort();
                }
            }
            catch (TimeoutException ex)
            {
                Common.MessageBox.MessageBox.NotifyError(
                    "Impossibile collegarsi al server: timeout nella connessione");
                Application.Exit();
                return;
            }
            catch (EndpointNotFoundException ex)
            {
                Common.MessageBox.MessageBox.NotifyError(
                    "Impossibile collegarsi al server: endpoint not found");
            }

            string tmp = string.Format("Gianos.NET management console: {0}", server ?? "");
            if (tmp.Length >= 64) tmp = tmp.Substring(0, 63);
            notifyIcon.Text = tmp;
        }

        void Main_ForceFullClientRefreshCallbackEvent()
        {
            if (this.InvokeRequired)
            {
                MethodInvoker method = Main_ForceFullClientRefreshCallbackEvent;
                this.Invoke(method);
                return;
            }

            // Force reconnection
            ReConnect(server, true);
        }

        private void Main_Load(object sender, System.EventArgs e)
        {
            changer.ShowPanel(true, true);

                if (Login())
                {
                    this.ConnectToServer(server);

                    cbServer.DataSource = sharedObj.Servers;
                    cbServer.DisplayMember = "name";
                    cbServer.ValueMember = "value";
                }
                else Close();
        }

        #region Intercetta gli eventi remoti e li propaga agli UC

        public void UpdateLog(Guid id, string msg)
        {
            if (this.InvokeRequired)
                BeginInvoke(new ThreadSafeLogUpdateDlg(UpdateLog), id, msg);
            else
            {
                foreach (NavItem p in cTile.Controls)
                {
                    Base b = p.Gestore;
                    if (b != null)
                        b.AppendToLog(id, msg);
                }
            }
        }

        private void Main_ServiceStatusChangeCallbackEvent(Guid svcId, RemotedClass.RemotedClassStatusEnum status)
        {
            if (this.InvokeRequired)
                BeginInvoke(new ThreadSafeServiceStatusDlg(Main_ServiceStatusChangeCallbackEvent), svcId, status);
            else
            {
                foreach (NavItem p in cTile.Controls)
                {
                    Base b = p.Gestore;
                    if (b != null && b.ServiceInformation.SvcId == svcId)
                        b.ChangeServiceStatus(svcId, status);
                }

                foreach (ServiceInfoExt si in svcInfo)
                    if (si.SvcId == svcId)
                        si.ActualServiceState = status;

                SignalSvcStatus();
            }
        }

        private void Main_LoopingStatusChangeCallbackEvent(Guid svcId, LoopingService.LoopingStateEnum status)
        {
            if (this.InvokeRequired)
                BeginInvoke(new ThreadSafeLoopingStatusDlg(Main_LoopingStatusChangeCallbackEvent), svcId, status);
            else
                foreach (NavItem p in cTile.Controls)
                {
                    Base b = p.Gestore;
                    if (b != null && b.ServiceInformation.SvcId == svcId)
                        b.ChangeLoopingStatus(svcId, status);
                }
        }

        private void Main_IdleCountdownChangeCallbackEvent(Guid svcId, int secBeforeNextExecution)
        {
            if (this.InvokeRequired)
                BeginInvoke(new ThreadSafeIdleCountdownDlg(Main_IdleCountdownChangeCallbackEvent), svcId,
                            secBeforeNextExecution);
            else
                foreach (NavItem p in cTile.Controls)
                {
                    Base b = p.Gestore;
                    if (b != null && b.ServiceInformation.SvcId == svcId)
                        b.ChangeIdleCountdown(svcId, secBeforeNextExecution);
                }
        }



        #endregion


        private void RegisterRemoteObject(ServiceInfoExt si)
        {
            Base uc =
                Activator.CreateInstance(Type.GetType(string.Format("ClientApp.Gestori.{0}", si.Gestore), true, false))
                as Base;
            NavItem nav = new NavItem(si.ServiceName, si.Version, uc, si.InstanceName);
            nav.MouseDown += nav_MouseDown;
   
            try
            {
                //tmp.Ping();
            }
            catch (Exception)
            {
                if (!Settings.Default.hideOfflineServices)
                    cTile.Controls.Add(nav);

                return;
            }


            if (uc != null)
            {
                ILoopingServiceGestore loopingServiceGestore = uc as ILoopingServiceGestore;
                if (loopingServiceGestore != null) loopingServiceGestore.RequireFullLog += uc_RequireFullLog;

                uc.ParentTile = nav;
                uc.SetServiceInformation(si);

                uc.Dock = DockStyle.Fill;

                uc.OnServiceStart += uc_OnServiceStart;
                uc.OnServiceStop += uc_OnServiceStop;

                // Forza avvio del looping service in idle
                
                if (loopingServiceGestore != null)
                {
                    loopingServiceGestore.OnLoopingForceStart += uc_OnLoopingForceStart;
                    loopingServiceGestore.OnLoopingPauseChanged += uc_OnLoopingPauseChanged;
                }

                cTile.Controls.Add(nav);
            }
        }

        private LogQueue uc_RequireFullLog(Guid logid)
        {
            return client.GetLog(logid);
        }


        private void uc_OnLoopingPauseChanged(Guid svcId, int newLoopingPause)
        {
            client.SetLoopingPause(svcId, newLoopingPause);
        }

        private void uc_OnLoopingForceStart(Base sender, Base.ServiceEventArgs args)
        {
            client.StartLoopingSession(args.svcId);
        }

        private void uc_OnServiceStop(Base sender, Base.ServiceEventArgs args)
        {
            client.StopService(args.svcId);
        }

        private void uc_OnServiceStart(Base sender, Base.ServiceEventArgs args)
        {
            client.StartService(args.svcId);
        }

        #region Login/Logout

        private void Logout()
        {
            client.LogOff();
        }

        private bool Login()
        {
            using (frmLogin frm = new frmLogin())
                if (frm.ShowDialog() == DialogResult.OK)
                {
                    server = frm.Server;
                    username = frm.Username;
                    accessPwd = frm.Password;

                    sharedObj.username = username;
                    sharedObj.password = accessPwd;

                    return true;
                }

            return false;
        }

        #endregion

        private void Main_Shown(object sender, EventArgs e)
        {

        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            notifyIcon.Visible = false;
            try
            {
                Logout();

                try
                {
                    client.Close();
                }
                finally
                {
                    if (client != null)
                        client.Abort();
                }
            }
            catch (Exception)
            {

            }
        }

        private void Main_SizeChanged(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                notifyIcon.Visible = true;
                Visible = false;
            }
        }


        private void TSMIShow_Click(object sender, EventArgs e)
        {
            this.Visible = true;
            WindowState = FormWindowState.Normal;
            Focus();
            notifyIcon.Visible = false;
        }

        private void TSMIExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        public void SignalSvcStatus()
        {
            RemotedClass.RemotedClassStatusEnum tmp = RemotedClass.RemotedClassStatusEnum.None;

            foreach (ServiceInfoExt si in svcInfo)
            {
                if (si.ActualServiceState == RemotedClass.RemotedClassStatusEnum.Started &&
                    tmp == RemotedClass.RemotedClassStatusEnum.None)
                    tmp = si.ActualServiceState;

                if (si.ActualServiceState == RemotedClass.RemotedClassStatusEnum.Stopped &&
                    tmp != RemotedClass.RemotedClassStatusEnum.Error)
                    tmp = si.ActualServiceState;

                if (si.ActualServiceState == RemotedClass.RemotedClassStatusEnum.Error)
                {
                    tmp = RemotedClass.RemotedClassStatusEnum.Error;
                    break;
                }
            }

            notifyIcon.Icon = Resources.Burn;
            switch (tmp)
            {
                case RemotedClass.RemotedClassStatusEnum.None:
                    imgStatus.Image = null;
                    break;
                case RemotedClass.RemotedClassStatusEnum.Started:
                    imgStatus.Image = Resources.power_on;
                    break;
                case RemotedClass.RemotedClassStatusEnum.Stopped:
                    imgStatus.Image = Resources.power_off;
                    break;
                case RemotedClass.RemotedClassStatusEnum.Error:
                    imgStatus.Image = Resources.warning;
                    notifyIcon.Icon = Resources.Burn_Red;
                    break;
            }
        }

        private void tmr_Tick(object sender, EventArgs e)
        {
            //SignalSvcStatus();
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            TSMIShow_Click(this, new EventArgs());
        }

        private void backLabel1_Click(object sender, EventArgs e)
        {
            changer.ShowPanel(true, true);
        }

        void nav_MouseDown(object sender, MouseEventArgs e)
        {
            NavItem item = sender as NavItem;
            if (item != null)
                if (e.Button == MouseButtons.Right)
                    item.OpenMenu(item, e.Location);
                else if (e.Button == MouseButtons.Left)
                {
                    pnlDetailContainer.Controls.Clear();
                    Base b = item.Gestore;
                    if (b != null)
                    {
                        pnlDetailContainer.Controls.Add(b);
                        backLabel1.Text =
                            string.Format(
                                //"<b><Size=+3><Color=DeepSkyBlue>{0}<br></COLOR></size></b><COLOR=Black><SIZE=+6>{1}</SIZE><COLOR>",
                                "{0} {1}", b.info.ServiceName, b.info.Version);
                    }
                    changer.ShowPanel(false, true);
                }
        }

        private void TSMIStopAll_Click(object sender, EventArgs e)
        {
            if (Common.MessageBox.MessageBox.AskForQuestion("Sei sicuro di fermare tutti i servizi?") == DialogResult.Yes)
            {
                ServiceInfo[] lst = client.GetServiceList();
                foreach (ServiceInfo info in lst)
                    if (info.ServiceStatus == RemotedClass.RemotedClassStatusEnum.Started)
                        client.StopService(info.SvcId);
            }
        }

        private void btnChangeService_Click(object sender, EventArgs e)
        {
            string srv = cbServer.SelectedValue as string;
            ReConnect(srv);
        }

        private void ReConnect(string srv, bool forceReconnect = false)
        {
            if (srv != server || forceReconnect || client.State == CommunicationState.Closed || client.State == CommunicationState.Faulted)
            {
                LogChangeCallbackEvent -= UpdateLog;
                ServiceStatusChangeCallbackEvent -= Main_ServiceStatusChangeCallbackEvent;
                LoopingStatusChangeCallbackEvent -= Main_LoopingStatusChangeCallbackEvent;
                IdleCountdownChangeCallbackEvent -= Main_IdleCountdownChangeCallbackEvent;
                ForceFullClientRefreshCallbackEvent -= Main_ForceFullClientRefreshCallbackEvent;

                if (client != null && client.State == CommunicationState.Opened)
                {
                    try
                    {
                        if (forceReconnect)
                            client.Close();
                        else
                            client.LogOff();
                    }
                    catch
                    {
                        
                    }
                }

                foreach (NavItem item in cTile.Controls)
                    item.Dispose();
                cTile.Controls.Clear();

                ConnectToServer(srv);
            }
        }

        private void btnAdmin_MouseDown(object sender, MouseEventArgs e)
        {
            ctxMain.Show(btnAdmin, e.X, e.Y);
        }

        private void TSMIShutdownRequest_Click(object sender, EventArgs e)
        {
            if (Common.MessageBox.MessageBox.AskForQuestion("Sei sicuro?") == DialogResult.Yes)
            {
                client.RequestServerShutdown();

                Common.MessageBox.MessageBox.NotifyExclamation(
                    "E' stata inviata la richiesta di shutdown al servizio. Chiudere il client o collegarlo ad un altro servizio.");
            }
        }

        private void TSMISvcCheck_Click(object sender, EventArgs e)
        {
            using (frmSvcCheck frm = new frmSvcCheck())
            {
                frm.StartPosition = FormStartPosition.CenterParent;
                frm.ShowDialog(this);
            }
        }

        private void ttMgr_Popup(object sender, PopupEventArgs e)
        {

        }
    }
}
